package belf.migrate.engine.job.verify;

import belf.migrate.api.exception.CatalogException;
import belf.migrate.api.table.schema.AbstractJdbcCatalog;
import belf.migrate.api.table.schema.AbstractSinkCatalog;
import belf.migrate.api.taskconf.JobContext;
import belf.migrate.api.type.DbObjectType;
import belf.migrate.api.type.LogLevelType;
import belf.migrate.api.util.PostLog;
import belf.migrate.api.util.VerifyObjectTypes;
import belf.migrate.core.number.NumberFormatUtil;
import belf.migrate.api.job.Job;
import belf.migrate.engine.job.model.verify.VerifyBaseModel;
import belf.migrate.engine.job.model.verify.VerifyViewResult;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;

import static belf.migrate.api.job.JobCode.*;

@Slf4j
public class VerifyViewTool {

    private Job job;

    private JobContext jobContext;

    private AbstractJdbcCatalog sourceCatalog;

    private AbstractSinkCatalog sinkCatalog;

    public VerifyViewTool(JobContext jobContext, Job job) {
        this.jobContext = jobContext;
        this.sourceCatalog = jobContext.getSourceCatalog();
        this.sinkCatalog = jobContext.getSinkCatalog();
        this.job = job;
    }

    public VerifyViewResult run() {

        if (!VerifyObjectTypes.isSupport(job.getTask().getTaskConf().getSourceConf().getCatalogName(),
                job.getTask().getTaskConf().getSinkConf().getCatalogName(), DbObjectType.VIEW)) {
            log.info("当前数据库不支持视图校验!");
            return null;
        }

        log.info("开始进行视图校验!");
        PostLog.sendLogModel(
                job.getJobId(),
                job.getTask().getTaskType().getName(),
                "开始进行视图校验!",
                LogLevelType.INFO.getType(),
                EXECUTING,
                jobContext.getProgress());

        VerifyViewResult viewResult = new VerifyViewResult();
        //获取数据库信息
        String sourceDatabaseName = job.getTask().getTaskConf().getSourceConf().getDatabaseName();
        String sinkDatabaseName = job.getTask().getTaskConf().getSinkConf().getDatabaseName();
        String sourceSchemaName = job.getTask().getTaskConf().getSourceConf().getSchemaName();
        String sinkSchemaName = job.getTask().getTaskConf().getSinkConf().getSchemaName();
        try {
            //获取源库和目标库的所有视图
            List<String> sourceViewNameList = sourceCatalog.listViewNames(sourceDatabaseName, sourceSchemaName);
            List<String> sinkViewNameList = sinkCatalog.listViewNames(sinkDatabaseName, sinkSchemaName);
            //判断源库视图是否为空
            if (sourceViewNameList == null) {
                if (sinkViewNameList == null) {
                    log.info("源数据库和目标数据库视图均为空!");
                    PostLog.sendLogModel(
                            job.getJobId(),
                            job.getTask().getTaskType().getName(),
                            "源数据库和目标数据库视图均为空!",
                            LogLevelType.INFO.getType(),
                            EXECUTING,
                            jobContext.getProgress());
                    viewResult.setCode(SUCCESS);
                    viewResult.setAllSize(0);
                    viewResult.setSuccessCount(0);
                    viewResult.setFailCount(0);
                    viewResult.setSuccessPercent("100.0%");
                    viewResult.setFailPercent("0.0%");
                    viewResult.setSuccess(true);
                } else {
                    log.info("源数据库视图为空，目标数据库视图不为空!");
                    PostLog.sendLogModel(
                            job.getJobId(),
                            job.getTask().getTaskType().getName(),
                            "源数据库视图为空，目标数据库视图不为空!",
                            LogLevelType.ERROR.getType(),
                            EXECUTING,
                            jobContext.getProgress());
                    viewResult.setCode(FAILTURE);
                    viewResult.setAllSize(0);
                    viewResult.setSuccessCount(0);
                    viewResult.setFailCount(0);
                    viewResult.setSuccessPercent("0.0%");
                    viewResult.setFailPercent("100.0%");
                    viewResult.setSuccess(false);
                }

            } else {
                int allSize = sourceViewNameList.size();
                int successCount = 0;
                int failCount = 0;
                Map<String, String> sourceLowerViewNameMap = sourceViewNameList.stream().collect(
                        Collectors.toMap(s -> s.toLowerCase(Locale.ROOT),
                                s -> s));
                Map<String, String> sinkLowerViewNameMap = null;
                if (sinkViewNameList != null) {
                    sinkLowerViewNameMap = sinkViewNameList.stream().collect(
                            Collectors.toMap(s -> s.toLowerCase(Locale.ROOT),
                                    s -> s));
                }
                List<VerifyBaseModel> viewBaseModelList = new ArrayList<>();
                int index = 0;
                //循环遍历源库视图
                for (String sourceViewName : sourceLowerViewNameMap.keySet()) {
                    jobContext.addProgress(DbObjectType.VIEW.getTypeName(), allSize);
                    long startTime = System.currentTimeMillis();
                    VerifyBaseModel viewBaseModel = new VerifyBaseModel();
                    if (sinkLowerViewNameMap != null && sinkLowerViewNameMap.containsKey(sourceViewName)) {
                        viewBaseModel.setSuccess(true);
                        viewBaseModel.setSourceName(sourceLowerViewNameMap.get(sourceViewName));
                        viewBaseModel.setSinkName(sinkLowerViewNameMap.get(sourceViewName));
                        successCount++;
                    } else {
                        viewBaseModel.setSuccess(false);
                        viewBaseModel.setSourceName(sourceLowerViewNameMap.get(sourceViewName));
                        failCount++;
                    }
                    //计算耗时
                    long endTime = System.currentTimeMillis();
                    String timeConsuming = String.valueOf((double) (endTime - startTime) / 1000);
                    viewBaseModel.setTimeConsuming(String.format("%s秒", timeConsuming));
                    viewBaseModelList.add(viewBaseModel);

                    index++;
                    PostLog.sendLogModel(
                            job.getJobId(),
                            job.getTask().getTaskType().getName(),
                            String.format("视图 %s 校验完成，目前进度：%s / %s.", sourceViewName, index, allSize),
                            LogLevelType.INFO.getType(),
                            EXECUTING,
                            jobContext.getProgress());
                }
                viewResult.setSuccess(successCount == allSize);
                viewResult.setAllSize(allSize);
                viewResult.setSuccessCount(successCount);
                viewResult.setFailCount(failCount);
                float successPercent = allSize == 0 ? 1 : (float)successCount / allSize;
                float failPercent = allSize == 0 ? 0 : (float)failCount / allSize;
                viewResult.setSuccessPercent(NumberFormatUtil.numberToString(successPercent, "0.0%"));
                viewResult.setFailPercent(NumberFormatUtil.numberToString(failPercent, "0.0%"));
                viewResult.setObjectList(viewBaseModelList);

            }

        } catch (CatalogException e) {
            log.error("视图校验失败！", e);
            PostLog.sendLogModel(
                    job.getJobId(),
                    job.getTask().getTaskType().getName(),
                    String.format("视图校验失败: %s", e.getMessage()),
                    LogLevelType.ERROR.getType(),
                    EXECUTING,
                    jobContext.getProgress());
            viewResult.setCode(FAILTURE);
        }
        return viewResult;
    }

}
